package com.sjsu.cloudmanager.server.service.aws;

import java.io.IOError;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.amazonaws.AmazonServiceException;
import com.amazonaws.auth.AWSCredentials;
import com.amazonaws.auth.PropertiesCredentials;
import com.amazonaws.services.ec2.AmazonEC2;
import com.amazonaws.services.ec2.AmazonEC2Client;
import com.amazonaws.services.ec2.model.*;
import com.sjsu.cloudmanager.model.CloudLab;
import com.sjsu.cloudmanager.model.ComputingMachine;
import com.sjsu.cloudmanager.model.ComputingMachineDescription;
import com.sjsu.cloudmanager.server.service.CloudManager;
import com.sjsu.cloudmanager.server.service.CloudServiceException;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;


public class AWSManager implements CloudManager {
    private static final Log log = LogFactory.getLog(CloudManager.class);
    private static final String AMI_INSTANCE_1 = "ami-7f418316";
    private static final String INSTANCE_TYPE_T1_MICRO = "t1.micro";
    static AmazonEC2 ec2;

    static {
        init();
    }

    private static void init() {
        try {
            AWSCredentials credentials = new PropertiesCredentials(
                    Thread.currentThread().getContextClassLoader().getResourceAsStream("AwsCredentials.properties"));
            ec2 = new AmazonEC2Client(credentials);
        } catch (IOException ioe) {
            log.error("failed to initialize ec2 client", ioe);
            throw new CloudServiceException(ioe);
        }
    }


    @Override
    public CloudLab createLab(CloudLab lab) {
        Set<ComputingMachine> machines = lab.getComputingMachines();

        for (ComputingMachine computingMachine : machines) {
            createMachine(computingMachine);
        }

        // TODO Add information to lab

        return lab;
    }

    @Override
    public void deleteLab(CloudLab lab) {
        // TODO Auto-generated method stub

    }

    @Override
    public void stopLab(CloudLab lab) {
        // TODO Auto-generated method stub

    }

    @Override
    public ComputingMachine createMachine(ComputingMachine machine) {

        // Abstract information from machine

        try {
            RunInstancesRequest rci = new RunInstancesRequest(AMI_INSTANCE_1, 1, 1);
            rci.setInstanceType(INSTANCE_TYPE_T1_MICRO);

            //rci.setKeyName(keyName)
            Reservation reservation = (ec2.runInstances(rci)).getReservation();
            machine.setMachineCloudId(reservation.getInstances().get(0).getInstanceId());

        } catch (AmazonServiceException ase) {
            log.error(String.format("Caught Exception: %s, Response Status Code: %s, Error Code: %s,  Request id; %s"
                    , ase.getMessage(), ase.getStatusCode(), ase.getErrorCode(), ase.getRequestId()), ase);
            throw new CloudServiceException(ase);
        }
        return machine;
    }


    @Override
    public void deleteMachine(ComputingMachine machine) {
        // TODO Auto-generated method stub
        try {
            TerminateInstancesRequest tir = new TerminateInstancesRequest();
            tir.withInstanceIds(machine.getMachineCloudId());
            ec2.terminateInstances(tir);

        } catch (AmazonServiceException ase) {
            log.error(String.format("Caught Exception: %s, Response Status Code: %s, Error Code: %s,  Request id; %s"
                    , ase.getMessage(), ase.getStatusCode(), ase.getErrorCode(), ase.getRequestId()), ase);
            throw new CloudServiceException(ase);
        }
    }

    @Override
    public void restartMachine(ComputingMachine machine) {
        // TODO Auto-generated method stub
        try {
            StartInstancesRequest startInstancesRequest = new StartInstancesRequest();
            startInstancesRequest.withInstanceIds(machine.getMachineCloudId());
            ec2.startInstances(startInstancesRequest);

        } catch (AmazonServiceException ase) {
            log.error(String.format("Caught Exception: %s, Response Status Code: %s, Error Code: %s,  Request id; %s"
                    , ase.getMessage(), ase.getStatusCode(), ase.getErrorCode(), ase.getRequestId()), ase);
            throw new CloudServiceException(ase);
        }
    }

    @Override
    public void stopMachine(ComputingMachine machine) {
        // TODO Auto-generated method stub
        try {
            StopInstancesRequest stopInstancesRequest = new StopInstancesRequest();
            stopInstancesRequest.withInstanceIds(machine.getMachineCloudId());
            ec2.stopInstances(stopInstancesRequest);

        } catch (AmazonServiceException ase) {
            log.error(String.format("Caught Exception: %s, Response Status Code: %s, Error Code: %s,  Request id; %s"
                    , ase.getMessage(), ase.getStatusCode(), ase.getErrorCode(), ase.getRequestId()), ase);
            throw new CloudServiceException(ase);
        }
    }



    @Override
    public ComputingMachineDescription getMachineDescription(ComputingMachine machine) {

    	ComputingMachineDescription machineDescription = new ComputingMachineDescription();
    	machineDescription.setMachineCloudId(machine.getMachineCloudId());

    	try {

            DescribeInstancesRequest describeInstancesRequest = new DescribeInstancesRequest();
            describeInstancesRequest.setInstanceIds(Arrays.asList(new String[]{machine.getMachineCloudId()}));
        	DescribeInstancesResult describeInstancesResult = ec2.describeInstances(describeInstancesRequest);
            List<Reservation> reservations = describeInstancesResult.getReservations();
            Set<Instance> instances = new HashSet<Instance>();

            for (Reservation reservation : reservations) {
                instances.addAll(reservation.getInstances());
            }

            for (Instance instance : instances) {
                if (instance.getInstanceId().equalsIgnoreCase(machine.getMachineCloudId())){
                	machineDescription.setImageId(instance.getImageId());
                	machineDescription.setPublicDns(instance.getPublicDnsName());
                    machineDescription.setStatus(instance.getState().getName());
                    //machineDescription.setOwner(instance.getLaunchTime());
                    machineDescription.setLaunchTime(instance.getLaunchTime());
                    break;
                }
            }

        } catch (AmazonServiceException ase) {
            log.error(String.format("Caught Exception: %s, Response Status Code: %s, Error Code: %s,  Request id; %s"
                    , ase.getMessage(), ase.getStatusCode(), ase.getErrorCode(), ase.getRequestId()), ase);
            throw new CloudServiceException(ase);
        }

        return machineDescription;

    }

}
